package cn.com.libertymutual.core.xml;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.io.Files;


public class CreateXml {
	private Document document;
	private Element root;
	private Map<String,String> namespacesMap = Maps.newHashMap();
	
	public CreateXml() throws ParserConfigurationException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		this.document = builder.newDocument();
	}
	
	public CreateXml( File file ) throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		this.document = builder.parse(file);
	}

	public void createContext( String rootStr ) {
		root = this.document.createElement(rootStr);
	
		document.appendChild( root );
	}
	
	public void createContext( String rootStr, String namespace ) {
		root = this.document.createElement(rootStr);
	
		if( !Strings.isNullOrEmpty(namespace) ) {
			root.setAttribute("xmlns", namespace);
			namespacesMap.put(namespace, "xmlns");
		}
		document.appendChild( root );
	}
	
	public Document getDocument() {
		return document;
	}
	
	public Element getElement() {
		return root;
	}
	
	public void createRow( List<Map<String,String>> list ) {
		Element elt = this.document.createElement("row");
		for (Map<String, String> map : list) {
			Element node = this.document.createElement(map.get("node"));
			node.setAttribute("desc", map.get("desc"));
			///node.setTextContent( map.get("value") );
			node.setNodeValue( map.get("value") );
			elt.appendChild( node );
		}
		root.appendChild( elt );
	}
	
	public List<Map<String,Object>> parseDocumentToMap( Element element ) {
		
		return null;
	}
	
	public void createElement( Element element, Object object ) {

		if( object instanceof List ) {
			List<?> lsit = (List<?>)object;
			for (Object object2 : lsit) {
				createElement(element, object2);
			}
		}
		else if( object instanceof Map ) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>)object;
			Set<String> set2 = map.keySet();
			Iterator<String> it2 = set2.iterator();
			while( it2.hasNext() ) {
				String key = it2.next();
				if( "namespace".equals(key) ) continue;
				String namespace = (String) map.get("namespace");
				Object value = map.get(key);

				///Element elt = this.document.createElementNS(namespace, key);
				Element elt = null;
				if( namespacesMap.containsKey(namespace) ) {
					String prefix = namespacesMap.get(namespace);
					elt = this.document.createElement( "xmlns".equals(prefix) ? key : String.format("%s:%s", prefix, key) );
				}
				else {
					if( !Strings.isNullOrEmpty(namespace) ) {
						String prefix = null;
						if( namespace.endsWith("/") ) {
							prefix = namespace.substring(0, namespace.length() - 1 );
							prefix = prefix.substring( prefix.lastIndexOf("/") + 1 ).replaceAll("_", "");
						}
						else if( namespace.substring(7).indexOf("/") > 0 ) {
							prefix = namespace.substring( namespace.lastIndexOf("/") + 1 ).replaceAll("_", "");
						}
						else {
							prefix = String.format("ns%d", namespacesMap.size());
						}

						namespacesMap.put(namespace, prefix);
						elt = this.document.createElement( String.format("%s:%s", prefix, key) );
						this.root.setAttribute("xmlns:"+prefix, namespace);
					}
					else elt = this.document.createElement( key );
				}
				
				//elt.setPrefix("ibcore");
				if( value instanceof List || value instanceof Map ) {
					createElement( elt, value );
				}
				else {
					String text = null;
					if( value  instanceof Integer ) {
						text = value == null ? "" : String.valueOf(value);
					}
					else text = (String) value;

					///elt.setTextContent( text );
					elt.setNodeValue( text );
					
				}
				//elt.setAttributeNS(namespace, key, key);
				element.appendChild( elt );
				
			}
			
			/*
			Set<Entry<String, Object>> set = map.entrySet();
			Iterator<Entry<String, Object>> it = set.iterator();
			while ( it.hasNext() ) {
				Entry<String, Object> entry = it.next();
				Object value = entry.getValue();
				String key = entry.getKey();
				
				if( !it.hasNext() ) break;//必须要namespace
				String namespace = (String) it.next().getValue();
				
				Element elt = this.document.createElementNS(namespace, key);
				//Element elt = this.document.createElement(entry.getKey());
				//elt.setPrefix("ibcore");

				if( value instanceof List || value instanceof Map ) {
					createElement( elt, value );
				}
				else {
					String text = null;
					if( value  instanceof Integer ) {
						text = value == null ? "" : String.valueOf(value);
					}
					else text = (String) value;

					elt.setTextContent( text );
					
				}
				element.appendChild( elt );
			}*/
		}
		else if( object instanceof String ) {
			Element elt = this.document.createElement((String)object);
			
			element.appendChild( elt );
		}

	}
	
	/**
	 * 保存XML
	 * @param file
	 * @param charset
	 * @update 2016-05-19 16:42 zhaoyu
	 * @throws Exception
	 */
	public void save( File file, Charset charset ) throws Exception{
		TransformerFactory tf = TransformerFactory.newInstance();
		BufferedWriter pw = null;
		try {
			Transformer transformer = tf.newTransformer();
			DOMSource source = new DOMSource(document);
			transformer.setOutputProperty(OutputKeys.ENCODING, charset.name());
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
			transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes");
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
			
			pw = Files.newWriter(file, charset);
			StreamResult result = new StreamResult( pw );
			transformer.transform(source, result);

		}finally {
			if( pw != null ) {
				try {
					pw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
